package com.jof.framework.jdbc;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;

/**
 * HOP对Statement的封装，添加sql语句执行的监控功能
 * @author Jonathan
 * @see java.sql.Statement
 */
public class HOPStatement implements Statement {
	/**
	 * 要监控的目标statement对象
	 */
	private Statement targetStatement;
	protected HOPJdbcConfiguration configuration;
	protected JdbcProfiler jdbcProfiler;
	protected HOPConnection connection;
	protected StringBuilder sql = new StringBuilder();
	protected JdbcLogContext logContext = new JdbcLogContext();
	public HOPStatement(HOPConnection connection,Statement targetStatement, HOPJdbcConfiguration configuration) {
		super();
		this.connection = connection;
		this.targetStatement = targetStatement;
		this.configuration = configuration;
		this.jdbcProfiler = this.configuration.getJdbcProfiler();
	}

	
	public <T> T unwrap(Class<T> iface) throws SQLException {
		return targetStatement.unwrap(iface);
	}

	
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		return targetStatement.isWrapperFor(iface);
	}

	
	public ResultSet executeQuery(final String sql) throws SQLException {
		this.sql.append(sql).append("\n");
		return doProfile(new ProfileStatementCallable<ResultSet>() {
			
			public ResultSet run(Statement statement) throws SQLException {
				return statement.executeQuery(sql);
			}
		});
	}

	
	public int executeUpdate(final String sql) throws SQLException {
		this.sql.append(sql).append("\n");
		return doProfile(new ProfileStatementCallable<Integer>() {
			
			public Integer run(Statement statement) throws SQLException {
				return statement.executeUpdate(sql);
			}
		});
	}

	
	public void close() throws SQLException {
		targetStatement.close();
	}

	
	public int getMaxFieldSize() throws SQLException {
		return targetStatement.getMaxFieldSize();
	}

	
	public void setMaxFieldSize(int max) throws SQLException {
		targetStatement.setMaxFieldSize(max);
	}

	
	public int getMaxRows() throws SQLException {
		return targetStatement.getMaxRows();
	}

	
	public void setMaxRows(int max) throws SQLException {
		targetStatement.setMaxRows(max);
	}

	
	public void setEscapeProcessing(boolean enable) throws SQLException {
		targetStatement.setEscapeProcessing(enable);
	}

	
	public int getQueryTimeout() throws SQLException {
		return targetStatement.getQueryTimeout();
	}

	
	public void setQueryTimeout(int seconds) throws SQLException {
		targetStatement.setQueryTimeout(seconds);
	}

	
	public void cancel() throws SQLException {
		targetStatement.cancel();
	}

	
	public SQLWarning getWarnings() throws SQLException {
		return targetStatement.getWarnings();
	}

	
	public void clearWarnings() throws SQLException {
		targetStatement.clearWarnings();
	}

	
	public void setCursorName(String name) throws SQLException {
		targetStatement.setCursorName(name);
	}

	
	public boolean execute(final String sql) throws SQLException {
		this.sql.append(sql).append("\n");
		return doProfile(new ProfileStatementCallable<Boolean>() {
			
			public Boolean run(Statement statement) throws SQLException {
				return statement.execute(sql);
			}
		});
	}

	
	public ResultSet getResultSet() throws SQLException {
		return targetStatement.getResultSet();
	}

	
	public int getUpdateCount() throws SQLException {
		return targetStatement.getUpdateCount();
	}

	
	public boolean getMoreResults() throws SQLException {
		return targetStatement.getMoreResults();
	}

	
	public void setFetchDirection(int direction) throws SQLException {
		targetStatement.setFetchDirection(direction);
	}

	
	public int getFetchDirection() throws SQLException {
		return targetStatement.getFetchDirection();
	}

	
	public void setFetchSize(int rows) throws SQLException {
		targetStatement.setFetchSize(rows);
	}

	
	public int getFetchSize() throws SQLException {
		return targetStatement.getFetchSize();
	}

	
	public int getResultSetConcurrency() throws SQLException {
		return targetStatement.getResultSetConcurrency();
	}

	
	public int getResultSetType() throws SQLException {
		return targetStatement.getResultSetType();
	}

	
	public void addBatch(String sql) throws SQLException {
		this.sql.append(sql).append("\n");
		targetStatement.addBatch(sql);
	}

	
	public void clearBatch() throws SQLException {
		targetStatement.clearBatch();
	}

	
	public int[] executeBatch() throws SQLException {
		return doProfile(new ProfileStatementCallable<int[]>() {
			
			public int[] run(Statement statement) throws SQLException {
				return targetStatement.executeBatch();
			}
		});
	}

	
	public Connection getConnection() throws SQLException {
		return this.connection;
	}

	
	public boolean getMoreResults(int current) throws SQLException {
		return targetStatement.getMoreResults(current);
	}

	
	public ResultSet getGeneratedKeys() throws SQLException {
		return doProfile(new ProfileStatementCallable<ResultSet>() {
			
			public ResultSet run(Statement statement) throws SQLException {
				return targetStatement.getGeneratedKeys();
			}
		});
	}

	
	public int executeUpdate(final String sql,final int autoGeneratedKeys) throws SQLException {
		this.sql.append(sql).append("\n");
		return doProfile(new ProfileStatementCallable<Integer>() {
			
			public Integer run(Statement statement) throws SQLException {
				return statement.executeUpdate(sql, autoGeneratedKeys);
			}
		});
	}

	
	public int executeUpdate(final String sql,final  int[] columnIndexes) throws SQLException {
		this.sql.append(sql).append("\n");
		return doProfile(new ProfileStatementCallable<Integer>() {
			
			public Integer run(Statement statement) throws SQLException {
				return statement.executeUpdate(sql, columnIndexes);
			}
		});
	}

	
	public int executeUpdate(final String sql, final String[] columnNames) throws SQLException {
		this.sql.append(sql).append("\n");
		return doProfile(new ProfileStatementCallable<Integer>() {
			
			public Integer run(Statement statement) throws SQLException {
				return statement.executeUpdate(sql, columnNames);
			}
		});
	}

	
	public boolean execute(final String sql,final int autoGeneratedKeys) throws SQLException {
		this.sql.append(sql).append("\n");
		return doProfile(new ProfileStatementCallable<Boolean>() {
			
			public Boolean run(Statement statement) throws SQLException {
				return statement.execute(sql, autoGeneratedKeys);
			}
		});
	}

	
	public boolean execute(final String sql,final int[] columnIndexes) throws SQLException {
		this.sql.append(sql).append("\n");
		return doProfile(new ProfileStatementCallable<Boolean>() {
			
			public Boolean run(Statement statement) throws SQLException {
				return statement.execute(sql, columnIndexes);
			}
		});
	}

	
	public boolean execute(final String sql, final String[] columnNames) throws SQLException {
		this.sql.append(sql).append("\n");
		return doProfile(new ProfileStatementCallable<Boolean>() {
			
			public Boolean run(Statement statement) throws SQLException {
				return statement.execute(sql, columnNames);
			}
		});
	}

	
	public int getResultSetHoldability() throws SQLException {
		return targetStatement.getResultSetHoldability();
	}

	
	public boolean isClosed() throws SQLException {
		return targetStatement.isClosed();
	}

	
	public void setPoolable(boolean poolable) throws SQLException {
		targetStatement.setPoolable(poolable);
	}

	
	public boolean isPoolable() throws SQLException {
		return targetStatement.isPoolable();
	}
	protected void prepareLogContext(){
		this.logContext.setStartTime(System.currentTimeMillis());
		this.logContext.setSql(sql);
	}
	
	private <T> T doProfile(ProfileStatementCallable<T> profileCallable) throws SQLException{
		prepareLogContext();
		this.logContext.setType(JdbcLogContext.STATEMENT);
		T result;
		try {
			result = profileCallable.run(targetStatement);
			this.logContext.setEndTime(System.currentTimeMillis());
		} catch (SQLException e) {
			this.logContext.setException(e);
			throw e;
		}finally{
			jdbcProfiler.log(this.logContext);
		}
		return result;
	}


	@Override
	public void closeOnCompletion() throws SQLException {
		// TODO Auto-generated method stub
		
	}


	@Override
	public boolean isCloseOnCompletion() throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}



}
